import { Generator, IGeneratorOptions } from '../Generator'
import { camelCase, pascalCase } from '../util'

export interface IIconColorHueInfo {
  type: 'hue'

  // 要替换的色度值
  hue: number

  // 默认的对比度增益，可设置，用于展示在默认值上
  saturation: number

  // 默认的明度增益，可设置，用于展示在默认值上
  light: number
}

export interface IIconColorReplaceInfo {
  type: 'color'

  // 要替换的颜色和展示的默认颜色
  color: string
}

export type IIconColorInfo = IIconColorHueInfo | IIconColorReplaceInfo

export interface IThemeColorHue extends IIconColorHueInfo {
  // 在主题里的配置的名字
  name: string

  // 是否为固定颜色，默认是false
  fixed?: boolean
}

export interface IThemeColorReplace extends IIconColorReplaceInfo {
  // 在主题里的配置的名字
  name: string

  // 是否为固定颜色，默认是false，为真的颜色无法改变
  fixed?: boolean

  // 是否绑定为CurrentColor，设置为true的情况下，这个组件的默认值会直接使用CurrentColor而不是配置的颜色
  currentColor?: boolean
}

export type IThemeColor = IThemeColorReplace | IThemeColorHue

export interface IIconThemeInfo {
  name: string

  // 可以填充的主题色
  fill: IThemeColor[]

  // 字符串传递固定颜色，数字为在fill里面的顺序，order的数量和顺序应该和colors里面一致
  order: number[]
}

export type StrokeLinecap = 'butt' | 'round' | 'square'

export type StrokeLinejoin = 'miter' | 'round' | 'bevel'

export interface IRuntimeOptions {
  // 是否导出类型
  useType?: boolean
  colors?: IIconColorInfo[]
  prefix?: string
  cssPrefix?: string
  // 如果为number，为默认值，不传递或者为0的情况下，不处理stroke问题
  stroke?: number
  strokeLinecap?: StrokeLinecap
  strokeLinejoin?: StrokeLinejoin
  theme?: IIconThemeInfo[]
  fixedSize?: boolean
  style?: boolean
}

export interface IRuntimeGeneratorOptions
  extends IGeneratorOptions,
    IRuntimeOptions {
  // 是否需要给Wrapper传递Name
  wrapperNeedName?: boolean

  // 是否需要编译RTL属性
  wrapperNeedRTL?: boolean
}

export abstract class RuntimeGenerator extends Generator {
  protected useType: boolean

  protected colors: IIconColorInfo[]

  protected stroke: number

  protected cssPrefix: string

  protected theme: IIconThemeInfo[]

  protected fixedSize: boolean

  protected style: boolean
  protected wrapperNeedName: boolean
  protected wrapperNeedRTL: boolean

  protected hueList: IIconColorHueInfo[] = []

  protected replaceList: IIconColorReplaceInfo[] = []

  protected hasDefaultTheme: boolean

  protected strokeLinecap?: StrokeLinecap

  protected strokeLinejoin?: StrokeLinejoin

  constructor(options: IRuntimeGeneratorOptions) {
    super(options)

    this.useType = options.useType || false
    this.stroke = options.stroke || 0
    this.cssPrefix = options.cssPrefix || 'sit'
    this.theme = options.theme || []
    this.fixedSize = options.fixedSize || false
    this.style = options.style || false
    this.colors = options.colors || []
    this.strokeLinecap = options.strokeLinecap
    this.strokeLinejoin = options.strokeLinejoin
    this.wrapperNeedName = options.wrapperNeedName || false
    this.wrapperNeedRTL = options.wrapperNeedRTL || false

    this.colors.forEach((item) => {
      if (item.type === 'hue') {
        this.hueList.push(item)
      } else {
        this.replaceList.push(item)
      }
    })

    this.hasDefaultTheme = this.theme.some((item) =>
      item.fill.some((item) => item.name)
    )
  }

  public process(): string {
    // 写头部的注释
    this.processHeaderComment()

    // 添加外部引用
    this.processPlatformImports()

    if (this.useType) {
      // 定义常量类型
      this.processConstTypes()

      // 生成内部函数属性类型参数
      this.processSvgType()

      // 生成配置信息类型
      this.processConfigType()

      // 生成属性类型
      this.processPropsType()

      // 生成平台外部类型
      this.processPlatformTypes()
    }

    // 生成默认配置信息
    this.processConfigData()

    // 生成工具函数
    this.processUtil()

    // 生成转换函数代码
    this.processConverter()

    // 平台业务代码
    this.processPlatformCode()

    // 生成Wrapper代码
    this.processWrapper()

    return this.getResult()
  }

  protected abstract processPlatformImports(): void

  protected abstract processPlatformTypes(): void

  protected abstract processPlatformCode(): void

  protected abstract processPlatformWrapper(): void

  protected processConstTypes(): void {
    const { theme, hueList, strokeLinejoin, strokeLinecap } = this

    // 描边连接类型
    if (strokeLinejoin) {
      this.writeLine('// 描边连接类型')
      this.writeLine(
        "export type StrokeLinejoin = 'miter' | 'round' | 'bevel';"
      )
      this.writeLine()
    }

    // 描边端点类型
    if (strokeLinecap) {
      this.writeLine('// 描边端点类型')
      this.writeLine("export type StrokeLinecap = 'butt' | 'round' | 'square';")
      this.writeLine()
    }

    // 主题
    if (theme.length) {
      this.writeLine('// 主题')
      this.writeLine(
        `export type Theme = ${theme
          .map((item) => `'${item.name}'`)
          .join(' | ')};`
      )
      this.writeLine()
    }

    // 渐变色处理
    if (hueList.length) {
      this.writeLine('// 渐变色格式化器')
      this.writeLine(
        'export type HueReplaceFormatter = (idx: number, s: number, l: number) => string'
      )

      this.writeLine()
      this.writeLine('// Hue类型')
      this.writeLine('export interface HSL {')
      this.indent(1)
      this.writeLine('h: number;')
      this.writeLine('s: number;')
      this.writeLine('l: number;')
      this.indent(-1)
      this.writeLine('}')
      this.writeLine()
    } else {
      this.writeLine()
    }
  }

  protected processSvgType(): void {
    const { replaceList, hueList } = this

    this.writeLine('// 包裹前的图标属性')
    this.writeLine(`export interface ${this.getInterfaceName('props', true)} {`)
    this.indent(1)

    this.writeLine()
    this.writeLine('// 当前图标的唯一Id')
    this.writeLine('id: string;')
    this.processSvgCommonType()

    if (replaceList.length) {
      this.writeLine()
      this.writeLine('// 换肤的颜色数组')
      this.writeLine('colors: string[];')
    }

    if (hueList.length) {
      this.writeLine()
      this.writeLine('// 渐变换肤的替换函数')
      this.writeLine('format: HueReplaceFormatter;')
    }

    this.indent(-1)
    this.writeLine('}')
    this.writeLine()
  }

  protected processConfigType(): void {
    this.writeLine('// 图标配置属性')
    this.writeLine(`export interface ${this.getInterfaceName('config')} {`)
    this.indent(1)

    this.processSvgCommonType()

    this.writeLine()
    this.writeLine('// CSS前缀')
    this.writeLine('prefix: string;')

    if (this.wrapperNeedRTL) {
      this.writeLine()
      this.writeLine('// RTL是否开启')
      this.writeLine('rtl: boolean;')
    }

    if (this.theme.length) {
      this.writeLine()
      this.writeLine('// 默认主题')
      this.writeLine('theme: Theme;')

      if (this.hasDefaultTheme) {
        this.writeLine()
        this.writeLine('// 主题默认颜色')
        this.writeLine('colors: {')
        this.indent(1)
        this.theme.forEach((item) => {
          if (!item.fill.some((item) => item.name)) {
            return
          }
          this.writeLine()
          this.writeLine(`${camelCase(item.name)}: {`)
          this.indent(1)

          item.fill.forEach((f) => {
            if (!f.name) {
              return
            }

            this.writeLine(
              `${camelCase(f.name)}: ${f.type === 'hue' ? 'HSL' : 'string'};`
            )
          })
          this.indent(-1)
          this.writeLine('};')
        })
        this.indent(-1)
        this.writeLine('};')
      }
    } else if (this.colors.length) {
      const list: string[] = []

      if (this.hueList.length) {
        list.push('HSL')
      }

      if (this.replaceList.length) {
        list.push('string')
      }

      this.writeLine()
      this.writeLine('// 默认颜色')

      if (list.length === 1) {
        this.writeLine(`colors: ${list.join(' | ')}[];`)
      } else {
        this.writeLine(`colors: Array<${list.join(' | ')}>;`)
      }
    }

    this.indent(-1)
    this.writeLine('}')
    this.writeLine()
  }

  protected processPropsType(): void {
    this.writeLine('// 图标基础属性')
    this.writeLine(`export interface ${this.getInterfaceName('base')} {`)
    this.indent(1)

    this.processSvgCommonType(true)

    if (this.theme.length) {
      this.writeLine()
      this.writeLine('// 默认主题')
      this.writeLine('theme?: Theme;')
    }

    // 添加类型
    const list: string[] = this.getColorTypes()

    if (list.length > 0) {
      this.writeLine()
      this.writeLine('// 填充色')
      this.writeLine(`fill?: ${list.join(' | ')};`)
    }

    this.indent(-1)
    this.writeLine('}')
    this.writeLine()
  }

  protected processSvgCommonType(optional: boolean = false): void {
    const { fixedSize, stroke, strokeLinecap, strokeLinejoin } = this

    if (fixedSize) {
      this.writeLine()
      this.writeLine('// 图标尺寸大小，默认1em')
      this.writeLine(`size${optional ? '?' : ''}: number | string;`)
    } else {
      this.writeLine()
      this.writeLine('// 图标尺寸大小不固定，长宽默认为1em')
      this.writeLine(`width${optional ? '?' : ''}: number | string;`)
      this.writeLine(`height${optional ? '?' : ''}: number | string;`)
    }

    if (stroke) {
      this.writeLine()
      this.writeLine('// 描边宽度')
      this.writeLine(`strokeWidth${optional ? '?' : ''}: number;`)
    }

    if (strokeLinecap) {
      this.writeLine()
      this.writeLine('// 描边端点类型')
      this.writeLine(`strokeLinecap${optional ? '?' : ''}: StrokeLinecap;`)
    }

    if (strokeLinejoin) {
      this.writeLine()
      this.writeLine('// 描边连接线类型')
      this.writeLine(`strokeLinejoin${optional ? '?' : ''}: StrokeLinejoin;`)
    }
  }

  protected processConfigData(): void {
    const {
      cssPrefix,
      fixedSize,
      stroke,
      strokeLinejoin,
      strokeLinecap,
      useType,
      theme,
    } = this

    this.writeLine('// 默认属性')
    this.writeLine(
      `export const DEFAULT_${this.prefix.toUpperCase()}_CONFIGS` +
        (useType ? `: ${this.getInterfaceName('config')}` : '') +
        ' = {'
    )
    this.indent(1)

    if (fixedSize) {
      this.writeLine("size: '1em',")
    } else {
      this.writeLine()
      this.writeLine('// 图标尺寸大小不固定，长宽默认为1em')
      this.writeLine("width: '1em',")
      this.writeLine("height: '1em',")
    }

    if (stroke) {
      this.writeLine(`strokeWidth: ${stroke},`)
    }

    if (strokeLinecap) {
      this.writeLine(`strokeLinecap: '${strokeLinecap}',`)
    }

    if (strokeLinejoin) {
      this.writeLine(`strokeLinejoin: '${strokeLinejoin}',`)
    }

    if (this.wrapperNeedRTL) {
      this.writeLine('rtl: false,')
    }

    if (theme.length) {
      this.writeLine(`theme: '${theme[0].name}',`)

      if (this.hasDefaultTheme) {
        this.writeLine('colors: {')
        this.indent(1)
        this.theme
          .filter((item) => item.fill.some((item) => item.name))
          .forEach((item, i, arr) => {
            this.writeLine(`${camelCase(item.name)}: {`)
            this.indent(1)

            item.fill
              .filter((item) => item.name)
              .forEach((f, i, arr) => {
                const name = f.name

                if (!name) {
                  return
                }

                if (f.type === 'hue') {
                  this.writeLine(`${camelCase(name)}: {`)
                  this.indent(1)
                  this.writeLine(`h: ${f.hue},`)
                  this.writeLine(`s: ${f.saturation},`)
                  this.writeLine(`l: ${f.light}`)
                  this.indent(-1)
                  this.writeLine('}' + (i !== arr.length - 1 ? ',' : ''))
                } else {
                  this.writeLine(
                    `${camelCase(name)}: '${f.color}'` +
                      (i !== arr.length - 1 ? ',' : '')
                  )
                }
              })
            this.indent(-1)
            this.writeLine(`}${i !== arr.length - 1 ? ',' : ''}`)
          })
        this.indent(-1)
        this.writeLine('},')
      }
    } else if (this.colors.length) {
      this.writeLine('colors: [')
      this.indent(1)
      this.colors.forEach((f, i, arr) => {
        if (f.type === 'hue') {
          this.writeLine('{')
          this.indent(1)
          this.writeLine(`h: ${f.hue},`)
          this.writeLine(`s: ${f.saturation},`)
          this.writeLine(`l: ${f.light}`)
          this.indent(-1)
          this.writeLine('}' + (i !== arr.length - 1 ? ',' : ''))
        } else {
          this.writeLine(`'${f.color}'` + (i !== arr.length - 1 ? ',' : ''))
        }
      })
      this.indent(-1)
      this.writeLine('],')
    }

    this.writeLine(`prefix: '${cssPrefix}'`)
    this.indent(-1)
    this.writeLine('};')
    this.writeLine()
  }

  protected processUtil(): void {
    this.writeLine(`function guid()${this.useType ? ': string' : ''} {`)
    this.indent(1)
    this.writeLine(
      `return '${this.prefix}-' + (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);`
    )
    this.indent(-1)
    this.writeLine('}')
    this.writeLine()
  }

  protected processConverter(): void {
    this.writeLine('// 属性转换函数')

    if (this.useType) {
      this.writeLine(
        `export function ${pascalCase(this.prefix)}Converter` +
          `(id: string, icon: ${this.getInterfaceName(
            'base'
          )}, config: ${this.getInterfaceName('config')})` +
          `: ${this.getInterfaceName('props', true)} {`
      )
    } else {
      this.writeLine(
        `export function ${pascalCase(
          this.prefix
        )}Converter (id, icon, config) {`
      )
    }

    this.indent(1)
    this.writeLine()

    if (this.colors.length) {
      this.writeLine(
        "const fill = typeof icon.fill === 'string' ? [icon.fill] : icon.fill || [];"
      )
    }

    if (this.replaceList.length) {
      if (this.useType) {
        this.writeLine('const colors: string[] = [];')
      } else {
        this.writeLine('const colors = [];')
      }
    }

    if (this.hueList.length) {
      if (this.useType) {
        this.writeLine('const hsl: HSL[] = [];')
      } else {
        this.writeLine('const hsl = [];')
      }
    }

    if (this.hasDefaultTheme) {
      this.writeLine()

      if (this.useType) {
        this.writeLine('const theme: Theme = icon.theme || config.theme;')
      } else {
        this.writeLine('const theme = icon.theme || config.theme;')
      }
      this.writeLine()
      this.writeLine('switch (theme) {')
      this.indent(1)

      this.theme.forEach((theme) => {
        this.writeLine(`case '${theme.name}':`)
        this.indent(1)

        this.colors.forEach((color, i) => {
          const order = theme.order[i]
          const fill = theme.fill[order]

          if (color.type === 'color') {
            if (fill.type !== 'color') {
              throw new Error('theme type and color type are different')
            }

            this.write('colors.push(')

            // 固定颜色
            if (fill.fixed) {
              this.write(`'${fill.color}'`)
            } else {
              this.write(
                `typeof fill[${order}] === 'string' ? fill[${order}] : `
              )

              if (fill.currentColor) {
                this.write("'currentColor'")
              } else {
                this.write(
                  `config.colors.${camelCase(theme.name)}.${camelCase(
                    fill.name
                  )}`
                )
              }
            }

            this.writeLine(');')
          } else {
            if (fill.type !== 'hue') {
              throw new Error('theme type and color type are different')
            }

            this.write('hsl.push(')

            // 固定颜色
            if (fill.fixed) {
              this.write(
                `{h: ${fill.hue}, s: ${fill.saturation}, l: ${fill.light}`
              )
            } else {
              this.write(
                `typeof fill[${order}] === 'object' ? fill[${order}] : `
              )
              this.write(
                `config.colors.${camelCase(theme.name)}.${camelCase(fill.name)}`
              )
            }

            this.writeLine(');')
          }
        })
        this.writeLine('break;')
        this.indent(-1)
      })
      this.indent(-1)
      this.writeLine('}')
    } else if (this.colors.length) {
      this.colors.forEach((fill, order) => {
        if (fill.type === 'color') {
          this.writeLine()
          this.writeLine(`if (typeof fill[${order}] === 'string') {`)
          this.indent(1)
          this.writeLine(`colors.push(fill[${order}]);`)
          this.indent(-1)
          this.writeLine()
          this.writeLine('} else {')
          this.indent(1)
          this.writeLine()
          this.writeLine(`if (typeof config.colors[${order}] !== 'string') {`)
          this.indent(1)
          this.writeLine(
            `throw new Error('config.colors[${order}] expect string to be HSL');`
          )
          this.indent(-1)
          this.writeLine('}')
          this.writeLine()
          this.writeLine(`colors.push(config.colors[${order}]);`)
          this.indent(-1)
          this.writeLine('}')
        } else {
          this.writeLine()
          this.writeLine("if (typeof fill[${order}] === 'string') {")
          this.indent(1)
          this.writeLine(`hsl.push(fill[${order}]);`)
          this.indent(-1)
          this.writeLine()
          this.writeLine('} else {')
          this.indent(1)
          this.writeLine()
          this.writeLine(`if (typeof config.colors[${order}] !== 'object') {`)
          this.indent(1)
          this.writeLine(
            `throw new Error('config.colors[${order}] expect HSL to be string');`
          )
          this.indent(-1)
          this.writeLine('}')
          this.writeLine()
          this.writeLine(`hsl.push(config.colors[${order}]);`)
          this.indent(-1)
          this.writeLine('}')
        }
      })
    }

    this.writeLine()
    this.writeLine('return {')
    this.indent(1)

    if (this.fixedSize) {
      this.writeLine('size: icon.size || config.size,')
    } else {
      this.writeLine()
      this.writeLine('// 图标尺寸大小不固定，长宽默认为1em')
      this.writeLine('width: icon.width || config.width,')
      this.writeLine('height: icon.height || config.height,')
    }

    if (this.stroke) {
      this.writeLine('strokeWidth: icon.strokeWidth || config.strokeWidth,')
    }

    if (this.strokeLinecap) {
      this.writeLine(
        'strokeLinecap: icon.strokeLinecap || config.strokeLinecap,'
      )
    }

    if (this.strokeLinejoin) {
      this.writeLine(
        'strokeLinejoin: icon.strokeLinejoin || config.strokeLinejoin,'
      )
    }

    if (this.replaceList.length) {
      this.writeLine('colors,')
    }

    if (this.hueList.length) {
      this.writeLine('format: (idx, s , l) => {')
      this.writeLine('},')
    }

    this.writeLine('id')
    this.indent(-1)
    this.writeLine('};')

    this.indent(-1)
    this.writeLine('}')
    this.writeLine()
  }

  protected processWrapper(): void {
    this.writeLine('// 图标Wrapper')
    this.write(`export function ${this.getTypeName('wrapper')}(`)

    if (this.wrapperNeedName) {
      if (this.useType) {
        this.write('name: string, ')
      } else {
        this.write('name, ')
      }
    }

    if (this.wrapperNeedRTL) {
      if (this.useType) {
        this.write('rtl: boolean, ')
      } else {
        this.write('rtl, ')
      }
    }

    if (this.useType) {
      this.write(`render: ${this.getTypeName('Render')}`)
    } else {
      this.write('render')
    }

    if (this.style) {
      this.write(', ')

      if (this.useType) {
        this.write(`cssRender?: ${this.getInterfaceName('props', true)}`)
      } else {
        this.write('cssRender')
      }
    }

    if (this.useType) {
      this.writeLine('): ' + this.getTypeName('') + ' {')
    } else {
      this.writeLine(') {')
    }

    this.indent(1)
    // 生成平台Wrapper代码
    this.processPlatformWrapper()
    this.indent(-1)
    this.writeLine('}')
  }

  protected getPropKeys(): string[] {
    const arr: string[] = []
    const {
      fixedSize,
      stroke,
      hueList,
      replaceList,
      strokeLinecap,
      strokeLinejoin,
    } = this

    if (fixedSize) {
      arr.push('size')
    } else {
      arr.push('width', 'height')
    }

    if (stroke) {
      arr.push('strokeWidth')
    }

    if (strokeLinecap) {
      arr.push('strokeLinecap')
    }

    if (strokeLinejoin) {
      arr.push('strokeLinejoin')
    }

    if (this.theme.length) {
      arr.push('theme')
    }

    if (hueList.length || replaceList.length) {
      arr.push('fill')
    }

    return arr
  }

  protected getClassName(key?: string): string {
    const { cssPrefix, prefix } = this
    return `'${cssPrefix}-${prefix}${key ? '-' + key : ''}'`
  }

  protected getColorTypes(): string[] {
    // 添加类型
    const list: string[] = []

    if (this.hueList.length) {
      list.push('HSL')
    }

    if (this.replaceList.length) {
      list.push('string')
    }

    if (list.length === 1) {
      list.push(list[0] + '[]')
    } else if (list.length > 1) {
      list.push(`Array<${list.join(' | ')}>`)
    }

    return list
  }
}
